package com.learning.thread.juc.queue;

import java.util.ArrayList;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.PriorityBlockingQueue;
import java.util.concurrent.TimeUnit;


/**
 * ClassName: ConditionTest
 * Description: 阻塞队列 BlockingQueue
 * ArrayBlockingQueue\LinkedBlockingQueue(可以不设上限)\PriorityBlockingQueue（带优先级的队列）
 * Date: 2016/11/26 17:45
 *
 * @author SAM SHO
 * @version V1.0
 */
public class BlockQueue {

    public static void main(String[] args) throws InterruptedException {
//        testApi();
        testApi2();
    }


    /**
     * 非优先级，先进先出(FIFO)
     */
    static void test() {

        // 内部数据实现，一定要设置 capacity 队列大小，是有界
        ArrayBlockingQueue<String> arrayBlockingQueue = new ArrayBlockingQueue<>(100);

        // 可以不设置 capacity 队列大小, 默认是 Integer.MAX_VALUE
        LinkedBlockingQueue<String> linkedBlockingQueue = new LinkedBlockingQueue<>();

        // 带优先级的队列,内部数组实现。默认初始化大小 DEFAULT_INITIAL_CAPACITY = 11
        PriorityBlockingQueue<String> priorityBlockingQueue = new PriorityBlockingQueue<>();

    }

    /**
     * 添加数据, 以 LinkedBlockingQueue 为例
     *
     * @throws InterruptedException
     */
    static void testApi() throws InterruptedException {

        LinkedBlockingQueue<String> blockingQueue = new LinkedBlockingQueue<>(2);

        // 1- 增加一个元索,如果队列已满，则抛出一个 IllegalStateException: Queue full 异常
        blockingQueue.add("a");
        System.out.println(blockingQueue);
        // blockingQueue.add("a2");
        // IllegalStateException: Queue full
        // blockingQueue.add("a3");

        // 2-添加一个元素并返回true, 如果队列已满，则返回false，不会有异常
        boolean b = blockingQueue.offer("b");
        System.out.println(b);
        boolean b2 = blockingQueue.offer("b2");
        System.out.println(b2);
        System.out.println(blockingQueue);

        // 3-队列满时指定阻塞时间，在规定时间内如果无法添加则返回false，不会有异常
        boolean c = blockingQueue.offer("c", 2, TimeUnit.SECONDS);
        System.out.println(c);
        boolean c1 = blockingQueue.offer("c1", 2, TimeUnit.SECONDS);
        System.out.println(c1);
        System.out.println(blockingQueue);

        // 清除数据
        blockingQueue.clear();

        // 4- 添加一个元素,如果队列满，则阻塞
        blockingQueue.put("d");
        System.out.println(blockingQueue);

        // 5- 批量添加数据
        ArrayList<String> list = new ArrayList<>();
        list.add("a");
        list.add("b");
        blockingQueue.clear();
        boolean b1 = blockingQueue.addAll(list);
        System.out.println(blockingQueue);
    }

    /**
     * 获取数据: ArrayBlockingQueue 为例
     *
     * @throws InterruptedException
     */
    static void testApi2() throws InterruptedException {

        ArrayBlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(2);

        blockingQueue.add("a");
        blockingQueue.add("b");


        // 1- 移除并返问队列头部的元素, 如果队列为空，则返回null，不会有异常
        String poll = blockingQueue.poll();
        System.out.println(poll);
        System.out.println(blockingQueue);

        // 2- 有数据直接返回；队列空时指定阻塞时间，在规定时间内还是没有数据，则返回 null
        String poll1 = blockingQueue.poll(2, TimeUnit.SECONDS);
        System.out.println(poll1);
        String poll2 = blockingQueue.poll(2, TimeUnit.SECONDS);
        System.out.println(poll2);
        System.out.println(blockingQueue);

        blockingQueue.add("a");
        blockingQueue.add("b");

        // 4-返回但不移除队列头部的元素, 如果队列为空，则返回 null，不会有异常
        String peek = blockingQueue.peek();
        System.out.println(peek);
        System.out.println(blockingQueue);

//        blockingQueue.clear();

        // 6-移除并返回队列头部的元素,如果队列为空，则阻塞
        blockingQueue.take();
        System.out.println(blockingQueue);

        // 内部调用 poll ,没有数据，抛出异常 NoSuchElementException
        String remove = blockingQueue.remove();
        boolean a = blockingQueue.remove("a");
        // 批量删除集合中的数据
        blockingQueue.removeAll(new ArrayList<>());


        blockingQueue.clear();
        blockingQueue.add("a");
        blockingQueue.add("b");

        // 7- 批量获取
        ArrayList<String> list = new ArrayList<>();
        int i = blockingQueue.drainTo(list);
        System.out.println(i);
        System.out.println(list);
    }

    static void testOther() {
        ArrayBlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(2);

        blockingQueue.add("a");
        blockingQueue.add("b");
        System.out.println(blockingQueue.size());
        System.out.println(blockingQueue.contains("a"));
        System.out.println(blockingQueue.remainingCapacity());

    }

    /**
     * 阻塞队列实现的线程同步
     */
    void test2() {
        final Business business = new Business();
        //子线程
        new Thread(() -> {

            for (int i = 1; i <= 5; i++) {
                business.sub(i);
            }

        }).start();

        // 主线程
        for (int i = 1; i <= 5; i++) {//5次循环以后，会阻塞。因为子线程已经结束 queue2 没有线程去取
            business.main(i);
        }
    }


    class Business {

        BlockingQueue<Integer> queue1 = new ArrayBlockingQueue<>(1);//队列1
        BlockingQueue<Integer> queue2 = new ArrayBlockingQueue<>(1);//队列2

        // 匿名构造方法，运行时机在任何构造方法之前
        {
            try {
                System.out.println("xxxxxdfsdsafdsa");
                queue2.put(1);// 先在队列2 queue2中放数据
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        /**
         * @param i
         */
        public void sub(int i) {
            try {
                queue1.put(1);//队列1 放数据
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            for (int j = 1; j <= 10; j++) {
                System.out.println("子线程 sequece of " + j + ",loop of " + i);
            }
            try {
                queue2.take();//队列2 取数据
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        /**
         * @param i
         */
        public void main(int i) {
            try {
                queue2.put(1);//队列2 放数据，如果先主线程进来，会阻塞
            } catch (InterruptedException e1) {
                e1.printStackTrace();
            }
            for (int j = 1; j <= 4; j++) {
                System.out.println("主线程  sequece of " + j + ",loop of " + i);
            }
            try {
                queue1.take();// 队列1 取数据
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
